package com.quanquan.user.service.autoCompute.service;

import DTO.PageDTO;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.alibaba.fastjson.JSON;
import com.quanquan.user.configBeans.RabbitSender;
import com.quanquan.user.service.autoCompute.constant.SymbolConst;
import com.quanquan.user.service.autoCompute.constant.SynConst;
import com.quanquan.user.service.autoCompute.dto.PushStatusDTO;
import com.quanquan.user.service.autoCompute.entity.GroupLabelRelation;
import com.quanquan.user.service.autoCompute.entity.LabelGroup;
import com.quanquan.user.service.autoCompute.entity.LabelGroupSynRecord;
import com.quanquan.user.service.autoCompute.entity.MemberLabel;
import com.quanquan.user.service.autoCompute.repository.GroupLabelRelationRepository;
import com.quanquan.user.service.autoCompute.repository.LabelGroupRepository;
import com.quanquan.user.service.autoCompute.repository.LabelGroupSynRecordRepository;
import com.quanquan.user.service.autoCompute.repository.MemberLabelRepository;
import com.quanquan.user.service.autoCompute.request.LabelGroupRequestBody;
import com.quanquan.user.service.autoCompute.request.MemberLabelRequestBody;
import com.quanquan.user.service.autoCompute.request.PushCouponRequestBody;
import exceptions.service.ServiceRuntimeException;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import utils.StringUtil;
import utils.UUIDGenerator;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import javax.transaction.Transactional;
import java.util.*;
import java.util.stream.Collectors;

@Log4j2
@Service
public class LabelGroupService {

    @Autowired
    private RabbitSender rabbitSender;

    @Autowired
    private LabelGroupRepository labelGroupRepository;

    @Autowired
    private GroupLabelRelationRepository groupLabelRelationRepository;

    @Autowired
    private MemberLabelService memberLabelService;

    @Resource
    private RedisTemplate<String, Long> redisTemplate;

    @Resource
    private MemberLabelRepository memberLabelRepository;

    @Autowired
    private LabelGroupSynRecordRepository labelGroupSynRecordRepository;

    /**
     * 创建会员画像
     * 创建完成去异步更新标签包含人数
     *
     * @param requestBody
     * @return
     * @throws ServiceRuntimeException
     */
    @Transactional(rollbackOn = {ServiceRuntimeException.class, Exception.class})
    public LabelGroup create(LabelGroupRequestBody requestBody) throws ServiceRuntimeException {

        List<MemberLabelRequestBody> labels = requestBody.getLabels();

        labels = labels.stream().filter(l -> StringUtil.isNotEmpty(l.getOption())).collect(Collectors.toList());

        if (labels == null || labels.isEmpty()) {
            throw new ServiceRuntimeException("标签不能为空");
        }

        List<MemberLabel> newLabels = new LinkedList<>();

        labels.forEach(o -> {
            MemberLabel newLabel = new MemberLabel();
            String labelRule = memberLabelService.convert(o);
            BeanUtil.copyProperties(o, newLabel, CopyOptions.create().setIgnoreNullValue(true));
            newLabel.setId(UUIDGenerator.getUUID());
            newLabel.setIsRemoved(false);
            newLabel.setLabelRule(labelRule);
            newLabel.setUpdateTime(new Date());
            newLabel.setCreateTime(new Date());
            newLabel.setSynStatus(SynConst.IN_PROGRESS);
            newLabels.add(newLabel);
        });

        Iterable<MemberLabel> memberLabels = memberLabelRepository.saveAll(newLabels);

        LabelGroup labelGroup = new LabelGroup();

        BeanUtil.copyProperties(requestBody, labelGroup, CopyOptions.create().setIgnoreNullValue(true));

        labelGroup.setCreateTime(new Date());

        labelGroup.setUpdateTime(new Date());

        labelGroup.setIsRemoved(false);

        labelGroup.setPushStatus(0);

        labelGroup.setId(UUIDGenerator.getUUIDforMySql());

        labelGroup.setSynStatus(SynConst.IN_PROGRESS);

        LabelGroup save = labelGroupRepository.save(labelGroup);

        if (null == save) {
            throw new ServiceRuntimeException("用户画像创建失败");
        }

        List<GroupLabelRelation> relationList = new LinkedList<>();

        memberLabels.forEach(l -> {
            GroupLabelRelation relation = new GroupLabelRelation();
            relation.setId(UUIDGenerator.getUUID());
            relation.setGroupId(save.getId());
            relation.setLabelId(l.getId());
            relationList.add(relation);
        });

        groupLabelRelationRepository.saveAll(relationList);

        this.syncLabelGroup(save.getId());

        return save;

    }

    /**
     * 分页获取结果
     *
     * @param requestBody
     * @return
     */
    public PageDTO<LabelGroup> list(LabelGroupRequestBody requestBody) throws ServiceRuntimeException {

        if (StringUtils.isEmpty(requestBody.getBelonging())) {
            // TODO 判断画像归属者
            throw new ServiceRuntimeException("请告诉我这是谁创建的画像");
        }

        if (requestBody.getPage() == null || requestBody.getPage() < 1) {
            throw new ServiceRuntimeException("分页参数不合法");
        }

        if (requestBody.getSize() == null || requestBody.getSize() < 1) {
            throw new ServiceRuntimeException("分页参数不合法");
        }

        Integer currPage = requestBody.getPage() - 1;

        Sort sort = new Sort(Sort.Direction.DESC, "updateTime");

        Pageable pageable = PageRequest.of(currPage, requestBody.getSize(), sort);

        Page<LabelGroup> page = labelGroupRepository.findAll((Specification<LabelGroup>) (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            // 创建具体查询条件
            if (StringUtils.isNotEmpty(requestBody.getKeyword())) {
                Predicate p1 = criteriaBuilder.like(root.get("name").as(String.class), "%" + requestBody.getKeyword() + "%");
                Predicate p2 = criteriaBuilder.or(criteriaBuilder.like(root.get("description").as(String.class), "%" + requestBody.getKeyword() + "%"), p1);
                predicates.add(p2);
            }
            predicates.add(criteriaBuilder.equal(root.get("isRemoved").as(Boolean.class), false));
//            predicates.add(criteriaBuilder.equal(root.get("belongings").as(String.class), requestBody.getBelongings()));
            return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();
        }, pageable);

        PageDTO<LabelGroup> pageResult = new PageDTO<>();

        pageResult.setPageSize(requestBody.getSize());

        pageResult.setPage(requestBody.getPage());

        pageResult.setKeyword(requestBody.getKeyword());

        pageResult.setTotal(page.getTotalElements());

        List<LabelGroup> content = page.getContent();

        content.forEach(c -> {
            Long successNum = null, failedNum = null, totalNum = null;
            if (redisTemplate.hasKey(SymbolConst.PUSH_SUCCESS + c.getId())) {
                successNum = Long.valueOf(redisTemplate.opsForValue().get(SymbolConst.PUSH_SUCCESS + c.getId()) + "");
            }
            if (redisTemplate.hasKey(SymbolConst.PUSH_FAILED + c.getId())) {
                failedNum = Long.valueOf(redisTemplate.opsForValue().get(SymbolConst.PUSH_FAILED + c.getId()) + "");
            }
            if (redisTemplate.hasKey(SymbolConst.PUSH_TOTAL + c.getId())) {
                totalNum = Long.valueOf(redisTemplate.opsForValue().get(SymbolConst.PUSH_TOTAL + c.getId()) + "");
            }
            if (null != successNum && null != failedNum && null != totalNum) {
                if (successNum + failedNum < totalNum) {
                    c.setPushStatus(3);
                }
            } else {
                c.setPushStatus(0);
            }
        });

        pageResult.setList(content);

        return pageResult;

    }

    /**
     * 根据id查询画像
     *
     * @param id
     * @return
     * @throws ServiceRuntimeException
     */
    public LabelGroup getById(String id) throws ServiceRuntimeException {

        if (StringUtils.isEmpty(id)) {
            throw new ServiceRuntimeException("Id不能为空");
        }

        LabelGroup labelGroup = labelGroupRepository.findById(id).orElse(null);

        if (null == labelGroup) {
            throw new ServiceRuntimeException("未找到相关用户画像");
        }

        List<GroupLabelRelation> byGroupId = groupLabelRelationRepository.findByGroupId(id);

        if (byGroupId == null || byGroupId.isEmpty()) {
            throw new ServiceRuntimeException("该画像未包含标签");
        }

        List<String> labelIds = byGroupId.stream().map(GroupLabelRelation::getLabelId).collect(Collectors.toList());

        List<MemberLabel> labels = memberLabelRepository.findByIdIn(labelIds);

        labelGroup.setLabels(labels);

        return labelGroup;

    }

    public LabelGroup getSimple(String id) throws ServiceRuntimeException {
        if (StringUtils.isEmpty(id)) {
            throw new ServiceRuntimeException("Id不能为空");
        }
        Optional<LabelGroup> o = labelGroupRepository.findById(id);
        if (!o.isPresent()) {
            throw new ServiceRuntimeException("未找到相关用户画像");
        } else {
            return o.get();
        }
    }

    /**
     * 修改标签组
     * 异步更新画像人数
     *
     * @param requestBody
     * @return
     * @throws ServiceRuntimeException
     */
    @Transactional(rollbackOn = {ServiceRuntimeException.class, Exception.class})
    public LabelGroup update(LabelGroupRequestBody requestBody) throws ServiceRuntimeException {

        String groupId = requestBody.getId();

        List<MemberLabelRequestBody> labels = requestBody.getLabels();
        //过滤掉非法的标签
        labels = labels.stream().filter(l -> StringUtil.isNotEmpty(l.getOption())).collect(Collectors.toList());

        if (StringUtils.isEmpty(groupId)) throw new ServiceRuntimeException("修改用户画像id不能为空");

        LabelGroup labelGroup = labelGroupRepository.findById(groupId).orElse(null);

        if (labelGroup == null) throw new ServiceRuntimeException("未查到该画像");

        BeanUtil.copyProperties(requestBody, labelGroup, CopyOptions.create().setIgnoreNullValue(true));

        labelGroup.setUpdateTime(new Date());

        log.info(JSON.toJSONString(labelGroup));

        LabelGroup save = labelGroupRepository.save(labelGroup);

        if (labels == null || labels.isEmpty()) throw new ServiceRuntimeException("子标签不能为空");

        List<GroupLabelRelation> byGroupId = groupLabelRelationRepository.findByGroupId(groupId);

        List<String> labelIds = byGroupId.stream().map(GroupLabelRelation::getLabelId).collect(Collectors.toList());

        List<String> newLabelIds = labels.stream().map(MemberLabelRequestBody::getId).filter(s -> StringUtil.isNotEmpty(s)).collect(Collectors.toList());

        List<String> reduce = labelIds.stream().filter(item -> !newLabelIds.contains(item)).collect(Collectors.toList());

        if (!reduce.isEmpty()) groupLabelRelationRepository.deleteAllByGroupIdAndLabelIdIsIn(groupId, reduce);

        List<MemberLabel> pendingSave = new LinkedList<>();

        labels.forEach(o -> {
            String labelRule = memberLabelService.convert(o);
            MemberLabel label = new MemberLabel();
            BeanUtil.copyProperties(o, label, CopyOptions.create().setIgnoreNullValue(true));
            label.setUpdateTime(new Date());
            if (StringUtil.isEmpty(o.getId())) {
                label.setCreateTime(new Date());
                label.setId(UUIDGenerator.getUUID());
            }
            label.setIsRemoved(false);
            label.setLabelRule(labelRule);
            label.setSynStatus(SynConst.IN_PROGRESS);
            pendingSave.add(label);
        });

        memberLabelRepository.saveAll(pendingSave);

        List<String> savedIds = pendingSave.stream().map(MemberLabel::getId).collect(Collectors.toList());

        List<String> increase = savedIds.stream().filter(item -> !labelIds.contains(item)).collect(Collectors.toList());

        if (!increase.isEmpty()) {
            List<GroupLabelRelation> relations = new LinkedList<>();
            increase.forEach(i -> {
                GroupLabelRelation relation = new GroupLabelRelation();
                relation.setGroupId(groupId);
                relation.setId(UUIDGenerator.getUUIDforMySql());
                relation.setLabelId(i);
                relations.add(relation);
            });
            groupLabelRelationRepository.saveAll(relations);
        }

        this.syncLabelGroup(save.getId());

        return save;

    }

    /**
     * 删除画像
     *
     * @param id
     */
    @Transactional(rollbackOn = {ServiceRuntimeException.class, Exception.class})
    public void remove(String id) throws ServiceRuntimeException {

        if (StringUtils.isEmpty(id)) {
            throw new ServiceRuntimeException("用户画像id不能为空");
        }

        LabelGroup labelGroup = labelGroupRepository.findById(id).orElse(null);

        if (null == labelGroup) {
            throw new ServiceRuntimeException("该画像不存在");
        }

        labelGroup.setIsRemoved(true);

        labelGroup.setUpdateTime(new Date());

        labelGroupRepository.save(labelGroup);

    }

    /**
     * 同步用户画像
     *
     * @param groupId
     */
    public LabelGroup syncLabelGroup(String groupId) throws ServiceRuntimeException {
        log.info("同步用户画像start");
        if (StringUtil.isEmpty(groupId)) {
            throw new ServiceRuntimeException("用户画像ID不能为空");
        }
        LabelGroup labelGroup = labelGroupRepository.findById(groupId).orElse(null);
        if (labelGroup == null) {
            throw new ServiceRuntimeException("未发现该画像");
        }
        labelGroup.setSynStatus(SynConst.IN_PROGRESS);
        labelGroup.setLastSyncTime(new Date());
        LabelGroup save = labelGroupRepository.save(labelGroup);
        Map<String, Object> properties = new HashMap<>();
        properties.put("labelGroupId", groupId);
        String exchange = "labelGroup.exchange";
        String routingKey = "labelGroup.sync";
        rabbitSender.sendMessageToMQ("同步用户画像", properties, exchange, routingKey);

        LabelGroupSynRecord labelGroupSynRecord = new LabelGroupSynRecord();
        labelGroupSynRecord.setGroupId(labelGroup.getId());
        labelGroupSynRecord.setSynStatus(SynConst.IN_PROGRESS);
        labelGroupSynRecord.setCreateTime(new Date());
        labelGroupSynRecord = labelGroupSynRecordRepository.saveAndFlush(labelGroupSynRecord);
        properties.put("synRecordId",labelGroupSynRecord.getId());

        rabbitSender.sendMessageToMQ("同步用户数据", properties, "labelGroup.exchange", "syncUser");
        return save;
    }


    public void pushCoupon(PushCouponRequestBody requestBody) throws ServiceRuntimeException {
        if (StringUtil.isEmpty(requestBody.getLabelGroupId())) {
            throw new ServiceRuntimeException("用户画像ID不能为空");
        }
        if (StringUtil.isEmpty(requestBody.getCouponId())) {
            throw new ServiceRuntimeException("优惠券ID不能为空");
        }
        Optional<LabelGroup> labelGroup = labelGroupRepository.findById(requestBody.getLabelGroupId());
        if (!labelGroup.isPresent()) {
            throw new ServiceRuntimeException("未发现该画像");
        }
        String pushSuccess = SymbolConst.PUSH_SUCCESS + requestBody.getLabelGroupId();
        String pushFailed = SymbolConst.PUSH_FAILED + requestBody.getLabelGroupId();
        String pushTotal = SymbolConst.PUSH_TOTAL + requestBody.getLabelGroupId();
        redisTemplate.opsForValue().set(pushTotal, labelGroup.get().getContainsNum());
        redisTemplate.opsForValue().set(pushSuccess, 0l);
        redisTemplate.opsForValue().set(pushFailed, 0l);
        Map<String, Object> properties = new HashMap<>();
        properties.put("groupId", requestBody.getLabelGroupId());
        properties.put("couponId", requestBody.getCouponId());
        properties.put("couponNum", requestBody.getCouponNum());
        properties.put("adminId", requestBody.getAdminId());
        properties.put("adminName", requestBody.getAdminName());
        String exchange = "pushLabelGroupCoupon.exchange";
        String routingKey = "pushLabelGroupCoupon.push";
        rabbitSender.sendMessageToMQ("推送优惠券", properties, exchange, routingKey);
    }

    public PushStatusDTO pushStatus(String groupId) {
        PushStatusDTO dto = new PushStatusDTO();
        String pushSuccess = SymbolConst.PUSH_SUCCESS + groupId;
        String pushFailed = SymbolConst.PUSH_FAILED + groupId;
        String pushTotal = SymbolConst.PUSH_TOTAL + groupId;
        Long successNum = null,failedNum = null, totalNum = null;
        if (redisTemplate.hasKey(pushSuccess)) {
            successNum = Long.valueOf(redisTemplate.opsForValue().get(pushSuccess) + "");
            dto.setSuccess(successNum);
        }
        if (redisTemplate.hasKey(pushFailed)) {
            failedNum = Long.valueOf(redisTemplate.opsForValue().get(pushFailed) + "");
            dto.setFailed(failedNum);
        }
        if (redisTemplate.hasKey(pushTotal)) {
            totalNum = Long.valueOf(redisTemplate.opsForValue().get(pushTotal) + "");
            dto.setTotal(totalNum);
        }
        if (null != pushSuccess && null != pushFailed && null != pushTotal) {
            if (failedNum + successNum < totalNum) {
                dto.setPushStatus(3);
            } else {
                dto.setPushStatus(1);
            }
        } else {
            dto.setPushStatus(0);
        }
        return dto;
    }

    public void save(LabelGroup labelGroup) {
        labelGroupRepository.save(labelGroup);
    }

}
